﻿#region Using directives

using System;
using System.Collections.Generic;
using System.Text;

using Weazel.Gezel.Model;
using Weazel.Gezel.Model.Errors;

#endregion

namespace Weazel.Gezel.Parser
{
  /// <summary>
  /// port_io_list: port_io_el
  ///        | port_io_list SEMICOLON port_io_el
  ///         ;
  /// 
  /// PortIoList : 		element	The name 'element' does not exist in the current context	

  ///     PortIoElement
  ///   | PortIoList SEMICOLON PortIoElement
  /// </summary>
  public static class ParsePortIoList
  {
    public static Set<Token> First = ParsePortIoElement.First;

    public static List<PortInfo> Parse(Set<Token> follow, Tokenizer t)
    {
      List<PortInfo> result = new List<PortInfo>();

      Set<Token> current = new Set<Token>(Token.Semicolon);
      current = current.Union(ParsePortIoElement.First);

      bool done = false;

      int portPosition = 0;

      do
      {
        result.AddRange(ParsePortIoElement.Parse(follow.Union(current), t, ref portPosition));

        t.Check(follow.Union(current));

        if (t.CurrentToken == Token.Semicolon)
        {
          t.Accept(Token.Semicolon, follow.Union(current));
        }
        else
        {
          done = true;
        }

      } while (!done);

      return result;  
    }
  }

  /// <summary>
  /// PortIoElement : 
  ///     IN PortIdList COLON SignalType
  ///   | OUT PortIdList COLON SignalType
  /// </summary>
  public static class ParsePortIoElement
  {
    public static readonly Set<Token> First = new Set<Token>(Token.In,Token.Out);

    public static List<PortInfo> Parse(Set<Token> follow, Tokenizer t, ref int portPosition)
    {
      Set<Token> current = new Set<Token>(
        Token.In,
        Token.Out,
        Token.Colon
      );

      current = current.Union(ParseList.First);
      current = current.Union(ParseSignalType.First);

      t.Check(follow.Union(current));

      current.Remove(Token.In, Token.Out);

      bool isInPort = false;
      bool hasError = false;

      if(t.CurrentToken == Token.In)
      {
        isInPort = true;
        t.Accept(Token.In, follow.Union(current));
      }
      else if(t.CurrentToken == Token.Out)
      {
        isInPort = false;
        t.Accept(Token.Out, follow.Union(current));
      }
      else
      {
        hasError = true;

        t.Errors.Add(
          new UnexpectedTokenParseError(
            t.CurrentPosition,
            t.CurrentToken,
            follow.Union(current)
          )
        );
      }

      current.Remove(ParseList.First);

      List<Model.CompoundPosition> positions = new List<CompoundPosition>();
      List<string> names = ParseList.Parse(follow.Union(current), t, ref positions);

      System.Diagnostics.Debug.Assert(names.Count == positions.Count);

      current.Remove(Token.Colon);
      t.Accept(Token.Colon, follow.Union(current));

      // if we encountered an error simply return nothing..
      if (hasError)
        return new List<PortInfo>();

      SignalTypeInfo type = ParseSignalType.Parse(follow, t);

      List<PortInfo> result = new List<PortInfo>();

      for (int i = 0; i < names.Count; i++)
      {
        result.Add(
					new PortInfo(
						names[i], 
						type, 
						isInPort, 
						portPosition, 
						positions[i].StartPosition, 
						positions[0].EndPosition
					)
				);
        portPosition++;
      }

      return result;
    }
  }

  public static class ParseSignalType
  {
    public static readonly Set<Token> First = new Set<Token>(
      Token.Ns,
      Token.Tc
    );

    public static SignalTypeInfo Parse(Set<Token> follow, Tokenizer t)
    {
      ParserDebug.WriteLine(
        "Entering 'ParseSignalType'",
        ConsoleColor.Red
      );

      Set<Token> current = new Set<Token>(
         Token.Ns,
         Token.Tc,
         Token.LeftParenthesis,
         Token.Number,
         Token.RightParenthesis
      );

      SignalTypeInfo result = new SignalTypeInfo();

      t.Check(follow.Union(current));

      current.Remove(Token.Ns, Token.Tc);

      if (t.CurrentToken == Token.Ns)
      {
        t.Accept(Token.Ns, follow.Union(current));
        result.Signed = false;
      }
      else if (t.CurrentToken == Token.Tc)
      {
        t.Accept(Token.Tc, follow.Union(current));
        result.Signed = true;
      }

      current.Remove(Token.LeftParenthesis);
      t.Accept(Token.LeftParenthesis, follow.Union(current));

      current.Remove(Token.Number);
      
      if(t.CurrentToken == Token.Number)
        result.Width = t.CurrentInt;

      t.Accept(Token.Number, follow.Union(current));      

      current.Remove(Token.RightParenthesis);
      t.Accept(Token.RightParenthesis, follow.Union(current));

      ParserDebug.WriteLine(
        "Leaving 'ParseSignalType'",
        ConsoleColor.Yellow
      );

      return result;
    }
  }
}
